跳到主要内容

Java 多线程-线程池 submit 和 execute 方法

submit 和 execute

submit 和 execute 这两个方法都能用来提交任务,所以这里先补充一下它们的区别,

execute 方法

在学习 submit 之前先来看下这个 execute 方法是怎么执行的

public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();

int c = ctl.get(); // 获取当前线程池的状态

// 当前线程数量小于 coreSize 时创建一个新的线程运行
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}

// 如果当前线程处于运行状态,并且写入阻塞队列成功
if (isRunning(c) && workQueue.offer(command)) {
// 双重检查,再次获取线程状态;
int recheck = ctl.get();
// 如果线程状态变了(非运行状态)就需要从阻塞队列移除任务,
// 并尝试判断线程是否全部执行完毕。同时执行拒绝策略。
if (! isRunning(recheck) && remove(command))
reject(command);
// 如果当前线程池为空就新创建一个线程并执行
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 如果判断为非运行状态,尝试新建线程,如果失败则执行拒绝策略。
else if (!addWorker(command, false))
reject(command);
}

如上的流程

5cd1d2ac0936c.jpg

submit 方法

1、submit 在执行过程中与 execute 不一样,不会抛出异常而是把异常保存在成员变量中,在 FutureTask.get 阻塞获取的时候再把异常抛出来。

2、submit 有返回值(使用的是 Callable 接口),而 execute 没有

并且当线程的执行过程中抛出了异常通常来说主线程也无法获取到异常的信息的,只有通过 ThreadFactory 主动设置线程的异常处理类才能感知到提交的线程中的异常信息。

使用例:

public class Temp {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2);
// 创建一个时间
Date time = new Date();
System.out.println(time.getTime());
Future<Date> future = pool.submit(new RunnableTest(time), time);

try {
// 只有调用了这个 future.get() 取得结果时才能触发异常
Date date = future.get();
System.out.println("修改时间为:" + date.getTime());

} catch (Exception e) {
// 只有上面执行了 future.get() 才会获取到异常,因为异常是存储在 Future 里面的
System.out.println(e.getCause().getMessage());
} finally {
pool.shutdown();
}
}

static class RunnableTest implements Runnable {

private final Date date;
// 需要通过构造函数传入 result
public RunnableTest(Date date) {
this.date = date;
}

@Override
public void run() {
date.setTime(System.currentTimeMillis());
// 测试抛出异常
throw new RuntimeException("测试抛出异常");
}

}
}

submit 提交的方式有如下三种(方法重载)

// 第一种方法是没有返回值的
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);
<T> Future<T> submit(Callable<T> task);

其中这个 Callable 在多线程那一篇文章有详细的介绍

public interface Callable<V> {
V call() throws Exception;
}

点进这个 submit 里面可以看到它的实现方法,它本质还是调用的 execute 方法,只是提交任务之前先把任务封装成 RunnableFuture 接口的对象

public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}

public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}

// ------------------------ 调用的这个 newTaskFor 方法 --------------------------------------------------

protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}

protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}

// ------------------------ 创建的这个 FutureTask 对象 -------------------------------------------------

// 在构造方法上,FutureTask 类对 Callable 和 Runnable 进行了一层封装。
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW;
}


public FutureTask(Callable<V> callable) {
if (callable == null)throw new NullPointerException();
this.callable = callable;
this.state = NEW;
}